package pl.un4giv3n.mightvsmagic.utils;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import pl.un4giv3n.mightvsmagic.entity.FightReport;
import pl.un4giv3n.mightvsmagic.entity.User;

public final class StatsUtil {
	private StatsUtil(){}
	private static Map<Long, UserStats> cache = new HashMap<Long, UserStats>();
	private static final Comparator<User> ANY_EXP_COMPARATOR = new AnyExpComparator();
	private static final Comparator<User> FIGHT_EXP_COMPARATOR = new FightExpComparator();
	private static final Comparator<User> MAGIC_EXP_COMPARATOR = new MagicExpComparator();
	private static final Comparator<User> FIGHTS_WON_COMPARATOR = new FightsWonComparator();
	private static final Comparator<User> FIGHTS_LOST_COMPARATOR = new FightsLostComparator();
	private static final Comparator<User> DMG_DONE_COMPARATOR = new DmgDoneComparator();
	private static final Comparator<User> DMG_TAKEN_COMPARATOR = new DmgTakenComparator();
	private static final Comparator<User> GOLD_WON_COMPARATOR = new GoldWonComparator();
	private static final Comparator<User> GOLD_LOST_COMPARATOR = new GoldLostComparator();
	private static final Comparator<User> LOGIN_COMPARATOR = new LoginComparator();
	
	public static Map<Long, UserStats> getCachedStats(){
		return Collections.unmodifiableMap(cache);
	}
	
	public static Map<Long, UserStats> getAllUserStats(EntityManager em){
		refreshCache(em);
		return Collections.unmodifiableMap(cache);
	}
	
	public static UserStats getUserStats(User user, EntityManager em){
		refreshCache(em);
		return cache.get(user.getId());
	}
	@SuppressWarnings("unchecked")
	public static void refreshCache(EntityManager em) {
		Query query = em.createNamedQuery("find.all.unblocked.users");
		List<User> users = query.getResultList();
		for (User user : users) {
			refreshUserStats(em, user);
		}
	}

	private static synchronized void refreshUserStats(EntityManager em, User user) {
		cache.put(user.getId(), getFreshUserStats(user, em));
	}
	
	@SuppressWarnings("unchecked")
	public static UserStats getFreshUserStats(User user,EntityManager em) {
		UserStats userStats = new UserStats();
		Query fightQuery = em.createNamedQuery("find.all.user.attacks");
		fightQuery.setParameter("id", user.getId().longValue());
		List<FightReport> fights = fightQuery.getResultList();
		for (FightReport fight : fights) {
			double attackerDamage = FightUtil.getAttackerDamage(fight);
			userStats.addDmgDone(attackerDamage);
			double defenderDamage = FightUtil.getDefenderDamage(fight);
			userStats.addDmgTaken(defenderDamage);
			if(attackerDamage > defenderDamage){
				userStats.addFightsWon(1);
				userStats.addGoldWon(fight.getReward().getRewardedGold());
			}else if (attackerDamage < defenderDamage){
				userStats.addFightsLost(1);
				userStats.addGoldLost(fight.getReward().getRewardedGold());
			}else{
				userStats.addDraws(1);
			}
		}
		fightQuery = em.createNamedQuery("find.all.user.defenses");
		fightQuery.setParameter("id", user.getId().longValue());
		fights = fightQuery.getResultList();
		for (FightReport fight : fights) {
			double attackerDamage = FightUtil.getAttackerDamage(fight);
			double defenderDamage = FightUtil.getDefenderDamage(fight);
			userStats.addDmgDone(defenderDamage);
			userStats.addDmgTaken(attackerDamage);
			if(attackerDamage > defenderDamage){
				userStats.addFightsLost(1);
				userStats.addGoldLost(fight.getReward().getRewardedGold());
			}else if (attackerDamage < defenderDamage){
				userStats.addFightsWon(1);
				userStats.addGoldWon(fight.getReward().getRewardedGold());
			}else{
				userStats.addDraws(1);
			}
		}
		return userStats;
	}
	
	public static void sort(List<User> users, String sortBy, String order){
		Comparator<? super User> comparator = getComparator(sortBy);
		Collections.sort(users, comparator);
		if("1".equals(order)){
			Collections.reverse(users);
		}
	}

	private static Comparator<? super User> getComparator(String sortBy) {
		Comparator<User> returnVal = ANY_EXP_COMPARATOR;
		if("login".equals(sortBy)){
			returnVal = LOGIN_COMPARATOR;
		}
		if("fightExp".equals(sortBy)){
			returnVal = FIGHT_EXP_COMPARATOR;
		}
		if("magicExp".equals(sortBy)){
			returnVal = MAGIC_EXP_COMPARATOR;
		}
		if("fightsWon".equals(sortBy)){
			returnVal = FIGHTS_WON_COMPARATOR;
		}
		if("fightsLost".equals(sortBy)){
			returnVal = FIGHTS_LOST_COMPARATOR;
		}
		if("goldWon".equals(sortBy)){
			returnVal = GOLD_WON_COMPARATOR;
		}
		if("goldLost".equals(sortBy)){
			returnVal = GOLD_LOST_COMPARATOR;
		}
		if("dmgDone".equals(sortBy)){
			returnVal = DMG_DONE_COMPARATOR;
		}
		if("dmgTaken".equals(sortBy)){
			returnVal = DMG_TAKEN_COMPARATOR;
		}
		return returnVal;
	}
	
	private static class AnyExpComparator implements Comparator<User>{

		@Override
		public int compare(User o1, User o2) {
			long thisVal = o1.getFightExp() + o1.getMagicExp();
			long anotherVal = o2.getFightExp() + o2.getMagicExp();
			return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
		}
		
	}
	
	private static class FightExpComparator implements Comparator<User>{

		@Override
		public int compare(User o1, User o2) {
			long thisVal = o1.getFightExp();
			long anotherVal = o2.getFightExp();
			return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
		}
	}
	
	private static class MagicExpComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			long thisVal = o1.getMagicExp();
			long anotherVal = o2.getMagicExp();
			return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
		}
	}
	private static class FightsWonComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			long thisVal = cache.get(o1.getId()).getFightsWon();
			long anotherVal = cache.get(o2.getId()).getFightsWon();
			return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
		}
	}
	private static class FightsLostComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			long thisVal = cache.get(o1.getId()).getFightsLost();
			long anotherVal = cache.get(o2.getId()).getFightsLost();
			return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
		}
	}
	private static class GoldWonComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			double thisVal = cache.get(o1.getId()).getGoldWon();
			double anotherVal = cache.get(o2.getId()).getGoldWon();
			return Double.compare(thisVal, anotherVal);
		}
	}
	private static class GoldLostComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			double thisVal = cache.get(o1.getId()).getGoldLost();
			double anotherVal = cache.get(o2.getId()).getGoldLost();
			return Double.compare(thisVal, anotherVal);
		}
	}
	private static class DmgDoneComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			double thisVal = cache.get(o1.getId()).getDmgDone();
			double anotherVal = cache.get(o2.getId()).getDmgDone();
			return Double.compare(thisVal, anotherVal);
		}
	}
	private static class DmgTakenComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			double thisVal = cache.get(o1.getId()).getDmgTaken();
			double anotherVal = cache.get(o2.getId()).getDmgTaken();
			return Double.compare(thisVal, anotherVal);
		}
	}
	private static class LoginComparator implements Comparator<User>{
		
		@Override
		public int compare(User o1, User o2) {
			String thisVal = o1.getLogin();
			String anotherVal = o2.getLogin();
			return thisVal.compareTo(anotherVal);
		}
	}

}
